#ifdef __EOS__
#include <rtthread.h>
#include "lombo_disp.h"
#else
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/version.h>
#include <linux/of_platform.h>

#include <drm/drmP.h>
#include <drm/drm_atomic.h>
#include <drm/drm_panel.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_fb_helper.h>
#include <video/mipi_display.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_modeset_helper.h>
#include <drm/lombo_drm.h>

#include "lombo_drv.h"
#include "lombo_wb.h"
#include "lombo_vo_manage.h"
#include "panel_lombo_simple.h"
#include "lombo_drm_generic.h"
#endif

#include "ei_comm_doss.h"
#include "ei_comm_vbuf.h"
#include "ei_comm_video.h"

#include "ei_math.h"
#include "ei_type.h"
#include "ei_common.h"
#include "ei_os.h"
#include "ei_comm_doss.h"

extern int DOSS_ModInit(void);
extern void DOSS_ModExit(void);
extern VIDEO_FRAME_FMT_S *doss_get_buf_fmt(PIXEL_FORMAT_E enPixelFormat);

EI_S32 doss_vo_get_vbuf_wh(VIDEO_FRAME_INFO_S *pstFrmInfo, EI_U32 *pu32Width, EI_U32 *pu32Height)
{
    EI_S32 s32Ret = EI_SUCCESS;

    if (!pstFrmInfo || !pu32Width || !pu32Height) {
        EI_TRACE_DOSS(EI_DBG_ERR, "pstFrmInfo or pu32Width or pu32Height null\n");
        return EI_ERR_VO_NULL_PTR;
    }

    switch (pstFrmInfo->enFrameType) {
    case MDP_FRAME_TYPE_COMMON:
        *pu32Width = pstFrmInfo->stCommFrameInfo.u32Width;
        *pu32Height = pstFrmInfo->stCommFrameInfo.u32Height;
        break;
    case MDP_FRAME_TYPE_VISS:
        *pu32Width = pstFrmInfo->stVissFrameInfo.stCommFrameInfo.u32Width;
        *pu32Height = pstFrmInfo->stVissFrameInfo.stCommFrameInfo.u32Height;
        break;
    case MDP_FRAME_TYPE_ISP:
        *pu32Width = pstFrmInfo->stIspFrameInfo.stCommFrameInfo.u32Width;
        *pu32Height = pstFrmInfo->stIspFrameInfo.stCommFrameInfo.u32Height;
        break;
    case MDP_FRAME_TYPE_IPPU:
        *pu32Width = pstFrmInfo->stIppuFrameInfo.stCommFrameInfo.u32Width;
        *pu32Height = pstFrmInfo->stIppuFrameInfo.stCommFrameInfo.u32Height;
        break;
    case MDP_FRAME_TYPE_DOSS:
        *pu32Width = pstFrmInfo->stDossFrameInfo.stCommFrameInfo.u32Width;
        *pu32Height = pstFrmInfo->stDossFrameInfo.stCommFrameInfo.u32Height;
        break;
    case MDP_FRAME_TYPE_VENC:
        *pu32Width = pstFrmInfo->stVencFrameInfo.stCommFrameInfo.u32Width;
        *pu32Height = pstFrmInfo->stVencFrameInfo.stCommFrameInfo.u32Height;
        break;
    default:
        *pu32Width = 0;
        *pu32Height = 0;
        EI_TRACE_DOSS(EI_DBG_ERR, "invalid frame buffer type\n");
        return EI_ERR_VO_ILLEGAL_PARAM;
    }

    if (pstFrmInfo->stVFrame.enRotation == ROTATION_90 || pstFrmInfo->stVFrame.enRotation == ROTATION_270) {
        EI_U32 u32Temp = *pu32Width;
        *pu32Width = *pu32Height;
        *pu32Height = u32Temp;
    }

    return s32Ret;
}

#ifdef __EOS__

struct wbc_buf {
    VBUF_BUFFER buf;
    VIDEO_FRAME_INFO_S info;
#ifndef __EOS__
    struct drm_framebuffer *fb;
#endif
};

struct wbc_user_buf {
    VIDEO_FRAME_INFO_S info;
    struct os_list_head node;
};

/**
 * @_SRC_PIPE: which display pipe wb from.
 * @_SRC_CROP_X/Y/W/H: crop of src, in pixel.
 * @_DST_WIDTH/HEIGHT: wb output resolution, in pixel.
 * @_CREATE_FB_QUEUE: create fb_queue with max count of buf of queue.
 */
enum {
    DOSS_WB_PARA_KEY_SRC_PIPE = 0,
    DOSS_WB_PARA_KEY_SRC_CROP_X,
    DOSS_WB_PARA_KEY_SRC_CROP_Y,
    DOSS_WB_PARA_KEY_SRC_CROP_W,
    DOSS_WB_PARA_KEY_SRC_CROP_H,
    DOSS_WB_PARA_KEY_DST_WIDTH,
    DOSS_WB_PARA_KEY_DST_HEIGHT,
    DOSS_WB_PARA_KEY_CREATE_FB_QUEUE,
    DOSS_WB_PARA_KEY_NUM_MAX,
};

typedef struct eiWBC_CTX_S {
    VO_WBC_SOURCE_S source;
    VO_WBC_ATTR_S attr;
    VO_WBC_MODE_E mode;

    EI_U32 depth;
    EI_U32 user_buf_num;
    struct os_list_head user_buf_head;
    os_spinlock_t user_buf_lock;
    os_wait_t user_buf_wait;

    MDP_CHN_S sink;

    EI_U32 enable;
    VBUF_POOL pool;
    struct os_work_struct work;

    struct wbc_buf wq_buf[2]; /* buf in workqueue */
} WBC_CTX_S;

#define MAX_DOSS_LAYER DC_WIN_NUM
#define MAX_DOSS_LAYER_NAME 16

typedef struct eiDISP_CTX_S {
    rt_device_t disp_dev;
    rt_device_disp_ops_t *disp_ops;
    disp_ctl_t *wctl[MAX_DOSS_LAYER];
    EI_BOOL enable[MAX_DOSS_LAYER];
    u32 valid_layer_num;
};

static struct eiDISP_CTX_S g_stDispCtx;

EI_S32 lombo_fbdev_enable(VO_DEV VoDev, VO_INTF_TYPE_E enIntfType,
        VO_SYNC_INFO_S *pstSyncInfo, VIDEO_FRAME_INFO_S *pstFrmInfo)
{
    EI_S32 ret;
    EI_U32 i, valid_layer_num;
    static char test_win_name[MAX_DOSS_LAYER_NAME] = {0};

    g_stDispCtx.disp_dev = rt_device_find(DISP_DEVICE_NAME);

    if (g_stDispCtx.disp_dev == NULL) {
        EI_TRACE_DOSS(EI_DBG_ERR, "lombo_fbdev_enable find disp dev err\n");
        return EI_FAILURE;
    }

    rt_device_open(g_stDispCtx.disp_dev, 0);

    g_stDispCtx.disp_ops = (rt_device_disp_ops_t *)(g_stDispCtx.disp_dev->user_data);
    if (g_stDispCtx.disp_ops == NULL) {
        EI_TRACE_DOSS(EI_DBG_ERR, "disp_ops is null\n");
        return EI_FAILURE;
    }

    rt_strncpy(test_win_name, "layer", MAX_DOSS_LAYER_NAME);

    ret = config_get_u32("disp", "mdp_valid_layer", &valid_layer_num);
    if (ret) {
        EI_TRACE_DOSS(EI_DBG_ERR, "mdp_valid_layer fail, use default[%d].", MAX_DOSS_LAYER);
        valid_layer_num = MAX_DOSS_LAYER;
    }

    if (valid_layer_num > MAX_DOSS_LAYER) {
        valid_layer_num = MAX_DOSS_LAYER;
    }
    g_stDispCtx.valid_layer_num = valid_layer_num;

    for (i = 0; i < valid_layer_num; i++) {
        test_win_name[5] = '0' + i;
        g_stDispCtx.wctl[i] = g_stDispCtx.disp_ops->disp_win_request(test_win_name);
        if (g_stDispCtx.wctl[i] == NULL) {
            EI_TRACE_DOSS(EI_DBG_ERR, "%s disp_win_request err\n", test_win_name);
            return EI_FAILURE;
        }
    }

    return EI_SUCCESS;
}

EI_S32 lombo_fbdev_disable(VO_DEV VoDev)
{
    EI_U32 i;
    EI_S32 ret;

    if (g_stDispCtx.disp_dev == NULL) {
        EI_TRACE_DOSS(EI_DBG_ERR, "lombo_fbdev_disable error\n");
        return EI_FAILURE;
    }

    for (i = 0; i < g_stDispCtx.valid_layer_num; i++) {
        ret = g_stDispCtx.disp_ops->disp_win_release(&g_stDispCtx.wctl[i]);
        if (ret) {
            EI_TRACE_DOSS(EI_DBG_ERR, "layer%d disp_win_release err\n", i);
            return EI_FAILURE;
        }
    }

    rt_device_close(g_stDispCtx.disp_dev);
    g_stDispCtx.disp_dev = NULL;
    return EI_SUCCESS;
}

int doss_drm_res_init(void)
{
    return EI_SUCCESS;
}

EI_S32 doss_vo_get_drm_client_file(EI_BOOL en)
{
    return EI_SUCCESS;
}

EI_U32 doss_vo_reset_plane_priority(EI_VOID)
{
    return EI_SUCCESS;
}

EI_U32 doss_vo_get_plane_priority(VO_LAYER VoLayer)
{
    EI_U32 i = 0;

    return i;
}

EI_U32 doss_vo_set_plane_priority(VO_LAYER VoLayer, EI_U32 priority)
{
    return EI_SUCCESS;
}

EI_S32 doss_set_csc(VO_LAYER VoLayer, VO_CSC_S *pstCSC)
{
    return EI_SUCCESS;
}

EI_S32 doss_get_csc(VO_LAYER VoLayer, VO_CSC_S *pstCSC)
{
    return EI_SUCCESS;
}

EI_U32 doss_vo_recaculate_rot_buffer_info(VIDEO_FRAME_INFO_S *pstFrameInfo,
    EI_U32 u32RotWidth, EI_U32 u32RotHeight, EI_U32 *pu32Lstr, EI_U32 *pu32Offset)
{
    VIDEO_FRAME_FMT_S *pstVideoFmt = EI_NULL;
    COMMON_FRAME_INFO_S *pstCommFrameInfo = EI_NULL;
    EI_U32 i = 0, u32PlaneNum = 0, u32PlaneSizeValid = 0;

    pstCommFrameInfo = &pstFrameInfo->stCommFrameInfo;
    pstVideoFmt = doss_get_buf_fmt(pstCommFrameInfo->enPixelFormat);
    if (!pstVideoFmt) {
        EI_TRACE_DOSS(EI_DBG_ERR, "get picure format error pixfmt:%d!\n",
            pstCommFrameInfo->enPixelFormat);
        return EI_INVALID_VALUE;
    }
    if (!u32RotWidth || !u32RotHeight) {
        EI_TRACE_DOSS(EI_DBG_ERR, "error picture size W:%d H:%d!\n", u32RotWidth, u32RotHeight);
        return EI_INVALID_VALUE;
    }

    u32PlaneNum = pstVideoFmt->u8DataPlanes;

    for (i = 0; i < u32PlaneNum; ++i) {
        pu32Lstr[i] =
            EI_ALIGN_UP((u32RotWidth * pstVideoFmt->u8WidthDepth[i] + 7) >> 3, pstCommFrameInfo->u32Align);
        u32PlaneSizeValid = pu32Lstr[i] * u32RotHeight * pstVideoFmt->u8HeightDepth[i] / 8;
        pu32Offset[i] = EI_ALIGN_UP(u32PlaneSizeValid, PAGE_SIZE);
    }

    return EI_SUCCESS;
}

static EI_S32 to_fourcc_format(PIXEL_FORMAT_E format, EI_U32 *fourcc)
{
    EI_S32 ret = EI_SUCCESS;

    switch (format) {
    case PIX_FMT_RGB_444:
        *fourcc = DISP_FORMAT_XRGB4444;
        break;
    case PIX_FMT_RGB_555:
        *fourcc = DISP_FORMAT_XRGB1555;
        break;
    case PIX_FMT_RGB_565:
        *fourcc = DISP_FORMAT_RGB565;
        break;
    case PIX_FMT_RGB_888:
        *fourcc = DISP_FORMAT_RGB888;
        break;
    case PIX_FMT_BGR_444:
        *fourcc = DISP_FORMAT_XBGR4444;
        break;
    case PIX_FMT_BGR_555:
        *fourcc = DISP_FORMAT_XBGR1555;
        break;
    case PIX_FMT_BGR_565:
        *fourcc = DISP_FORMAT_BGR565;
        break;
    case PIX_FMT_BGR_888:
        *fourcc = DISP_FORMAT_BGR888;
        break;
    case PIX_FMT_ARGB_1555:
        *fourcc = DISP_FORMAT_ARGB1555;
        break;
    case PIX_FMT_ARGB_4444:
        *fourcc = DISP_FORMAT_ARGB4444;
        break;
#if 0
    case PIX_FMT_ARGB_8565:
        *fourcc = DISP_FORMAT_RGB565_A8;
        break;
#endif
    case PIX_FMT_ARGB_8888:
        *fourcc = DISP_FORMAT_ARGB8888;
        break;
    case PIX_FMT_ABGR_1555:
        *fourcc = DISP_FORMAT_ABGR1555;
        break;
    case PIX_FMT_ABGR_4444:
        *fourcc = DISP_FORMAT_ABGR4444;
        break;
#if 0
    case PIX_FMT_ABGR_8565:
        *fourcc = DISP_FORMAT_BGR565_A8;
        break;
#endif
    case PIX_FMT_ABGR_8888:
        *fourcc = DISP_FORMAT_ABGR8888;
        break;
    case PIX_FMT_RGB_BAYER_8BITS:
    case PIX_FMT_RGB_BAYER_10BITS:
    case PIX_FMT_RGB_BAYER_12BITS:
    case PIX_FMT_RGB_BAYER_14BITS:
    case PIX_FMT_RGB_BAYER_16BITS:
    case PIX_FMT_RGB_BAYER_20BITS:
    case PIX_FMT_RGB_BAYER_10BITS_CMP:
    case PIX_FMT_RGB_BAYER_12BITS_CMP:
    case PIX_FMT_RGB_BAYER_14BITS_CMP:
        ret = EI_ERR_VO_NOT_SUPPORT;
        break;
    case PIX_FMT_YUV_SEMIPLANAR_422:
        *fourcc = DISP_FORMAT_NV16;
        break;
    case PIX_FMT_YUV_SEMIPLANAR_420:
        *fourcc = DISP_FORMAT_NV12;
        break;
    case PIX_FMT_YUV_SEMIPLANAR_444:
        ret = EI_ERR_VO_NOT_SUPPORT;
        break;
    case PIX_FMT_YVU_SEMIPLANAR_422:
        *fourcc = DISP_FORMAT_NV61;
        break;
    case PIX_FMT_YVU_SEMIPLANAR_420:
        *fourcc = DISP_FORMAT_NV21;
        break;
    case PIX_FMT_YVU_SEMIPLANAR_444:
        ret = EI_ERR_VO_NOT_SUPPORT;
        break;
    case PIX_FMT_YUV_PLANAR_422:
        *fourcc = DISP_FORMAT_YUV422;
        break;
    case PIX_FMT_YUV_PLANAR_420:
        *fourcc = DISP_FORMAT_YUV420;
        break;
    case PIX_FMT_YUV_PLANAR_444:
        *fourcc = DISP_FORMAT_YUV444;
        break;
    case PIX_FMT_YVU_PLANAR_422:
        *fourcc = DISP_FORMAT_YVU422;
        break;
    case PIX_FMT_YVU_PLANAR_420:
        *fourcc = DISP_FORMAT_YVU420;
        break;
    case PIX_FMT_YVU_PLANAR_444:
        *fourcc = DISP_FORMAT_YVU444;
        break;
    case PIX_FMT_YUYV_PACKAGE_422:
        *fourcc = DISP_FORMAT_YVYU;
        break;
    case PIX_FMT_YVYU_PACKAGE_422:
        *fourcc = DISP_FORMAT_YUYV;
        break;
    case PIX_FMT_UYVY_PACKAGE_422:
        *fourcc = DISP_FORMAT_UYVY;
        break;
    case PIX_FMT_VYUY_PACKAGE_422:
        *fourcc = DISP_FORMAT_VYUY;
        break;
    case PIX_FMT_YYUV_PACKAGE_422:
    case PIX_FMT_YYVU_PACKAGE_422:
    case PIX_FMT_UVYY_PACKAGE_422:
    case PIX_FMT_VUYY_PACKAGE_422:
    default:
        ret = EI_ERR_VO_NOT_SUPPORT;
        break;
    }

    return ret;
}

static void doss_vo_set_plane_state(VO_LAYER VoLayer, EI_BOOL enable)
{
    disp_io_ctrl_t dic = { 0 };

    dic.dc_index = 0;
    dic.dctl = g_stDispCtx.wctl[VoLayer];

    if (enable)
        rt_device_control(g_stDispCtx.disp_dev, DISP_CMD_ENABLE_DC_WIN, &dic);
    else
        rt_device_control(g_stDispCtx.disp_dev, DISP_CMD_DISABLE_DC_WIN, &dic);
}

int doss_vo_setplane(VO_LAYER VoLayer, VIDEO_FRAME_INFO_S *pstFrmInfo,
    int32_t crtc_x, int32_t crtc_y, uint32_t crtc_w, uint32_t crtc_h,
    uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h)
{
    dc_win_data_t win_data;
    u32 pixel_format;
    VIDEO_FRAME_S *pstVFrame = &pstFrmInfo->stVFrame;
    COMMON_FRAME_INFO_S *pstCommFrameInfo = &pstFrmInfo->stCommFrameInfo;
    ROTATION_E enRot = pstFrmInfo->stVFrame.enRotation;
    EI_U32 au32Lstr[3] = { 0 }, au32Offset[3] = { 0 };
    EI_U32 u32Width = 0, u32Height = 0;

    if (g_stDispCtx.disp_dev == NULL) {
        EI_TRACE_DOSS(EI_DBG_ERR, "disp_dev is null\n");
        return EI_FAILURE;
    }

    if (VoLayer >= MAX_DOSS_LAYER) {
        EI_TRACE_DOSS(EI_DBG_ERR, "invalid VoLayer[%d]\n", VoLayer);
        return EI_FAILURE;
    }

    /* only update valid layers */
    if (VoLayer >= g_stDispCtx.valid_layer_num) {
        return EI_SUCCESS;
    }

    if (doss_vo_get_vbuf_wh(pstFrmInfo, &u32Width, &u32Height)) {
        return EI_FAILURE;
    }

    if (!EI_VBUF_CHECK_BUFFER(pstFrmInfo->u32BufferId)) {
        if (VoLayer < g_stDispCtx.valid_layer_num &&
            g_stDispCtx.enable[VoLayer]) {
            g_stDispCtx.enable[VoLayer] = EI_FALSE;
            doss_vo_set_plane_state(VoLayer, EI_FALSE);
        }
        return EI_SUCCESS;
    }

    if (enRot == ROTATION_90 || enRot == ROTATION_270) {
        if (doss_vo_recaculate_rot_buffer_info(pstFrmInfo, u32Width, u32Height, au32Lstr, au32Offset)) {
            EI_TRACE_DOSS(EI_DBG_ERR, "recal rot buffer info err\n");
            return EI_NULL;
        }
    } else {
        au32Lstr[0] = pstVFrame->u32PlaneStride[0];
    }

    if (!g_stDispCtx.enable[VoLayer]) {
        g_stDispCtx.enable[VoLayer] = EI_TRUE;
        doss_vo_set_plane_state(VoLayer, EI_TRUE);
    }

#if 0
    EI_TRACE_DOSS(EI_DBG_INFO, "VirAddr[%lx %lx]\n",
        pstVFrame->ulPlaneVirAddr[0], pstVFrame->ulPlaneVirAddr[1]);

    EI_TRACE_DOSS(EI_DBG_INFO, "crtc[%d %d %d %d]\n", crtc_x, crtc_y, crtc_w, crtc_h);
    EI_TRACE_DOSS(EI_DBG_INFO, "src[%d %d %d %d]\n", src_x >> 16, src_y >> 16, src_w >> 16, src_h >> 16);
    EI_TRACE_DOSS(EI_DBG_INFO, "wxh[%d %d]\n", au32Lstr[0], u32Height);
#endif

    to_fourcc_format(pstCommFrameInfo->enPixelFormat, &pixel_format);

    rt_memset(&win_data, 0, sizeof(dc_win_data_t));

    win_data.dma_addr = (u32)pstVFrame->ulPlaneVirAddr[0];
    win_data.chroma_dma_addr = (u32)pstVFrame->ulPlaneVirAddr[1];
    win_data.chroma_x_dma_addr = (u32)pstVFrame->ulPlaneVirAddr[2];
    win_data.pixel_format = pixel_format;
    win_data.pixel_order = DC_PO_NORMAL;
    win_data.bpp = 32;

    win_data.crtc_x = crtc_x;
    win_data.crtc_y = crtc_y;
    win_data.crtc_width = crtc_w;
    win_data.crtc_height = crtc_h;

    win_data.fb_x = src_x >> 16;
    win_data.fb_y = src_y >> 16;
    win_data.fb_width = src_w >> 16;
    win_data.fb_height = src_h >> 16;

    win_data.src_width = au32Lstr[0];
    win_data.src_height = u32Height;

    win_data.update_flag = true;

    g_stDispCtx.disp_ops->disp_win_update(g_stDispCtx.wctl[VoLayer], &win_data);

    return EI_SUCCESS;
}

int doss_vo_atomic_commit(EI_VOID)
{
    return EI_SUCCESS;
}

WBC_CTX_S g_stWbcCtx;

EI_VOID wbc_worker(struct os_work_struct *work)
{
    return;
}

EI_S32 wbc_dev_check(EI_VOID)
{
    return EI_SUCCESS;
}

EI_S32 wbc_set_param(EI_U32 key, EI_U64 value)
{
    return EI_SUCCESS;
}

int doss_driver_init(void)
{
    DOSS_ModInit();

    os_printk("%s\n", "doss init ok");
    return 0;
}

#else

extern EI_S32 wbc_put_buffer(VBUF_BUFFER Buffer, MOD_ID_E enModId);
extern EI_S32 wbc_get_buffer(VBUF_POOL Pool, MOD_ID_E enModId, EI_U32 u32TimeoutMs,
    VBUF_BUFFER *pBuffer);
extern EI_S32 wbc_get_frame_info(VIDEO_FRAME_INFO_S *pstVFrameInfo);
extern EI_S32 wbc_notify_sink(MDP_CHN_S *pstSinkChn,       EI_U32 u32Flag, EI_VOID *pPrivate);
extern EI_S32 wbc_ref_add(VBUF_BUFFER Buffer, MOD_ID_E enModId);

extern struct device *dev;

struct drm_mode_katomic_req_item {
    struct drm_mode_object *obj;
    struct drm_property *prop;
    EI_U64 value;
};

struct drm_mode_katomic_req {
    EI_U32 cursor;
    EI_U32 size_items;
    struct drm_mode_katomic_req_item *items;
};

struct ei_drm_dev {
    struct drm_device *drm_dev;
    struct drm_file *file_priv;
    struct drm_plane **planes, **planes_bak;
    EI_BOOL bInited;
    EI_BOOL bSkipBoot;
    EI_BOOL bSyncTE;
    struct drm_framebuffer **fbs;
    struct drm_mode_atomic_k *katomic;
    struct drm_mode_katomic_req *req;
    struct drm_property_blob *blob;
    struct drm_connector **connectors;
    EI_U32 num_connectors;

    /* for wbc */
    struct lombo_wb *wb;
    struct lombo_wb_funcs *wb_funcs;
};

struct device *dev = EI_NULL;
static struct ei_drm_dev *g_pstDrmDev;
struct panel_lombo_simple_desc desc = { 0 };

#define DOSS_DRV_GET_DRM_CTX() (g_pstDrmDev)

/*--------------------lombo vbuf drm fb start----------------------*/
static struct drm_gem_object *lombo_drm_gem_import_vbuf(
    struct drm_device *drm, size_t buf_size, dma_addr_t buf_paddr)
{
    struct drm_gem_cma_object *cma_obj = NULL;
    struct drm_gem_object *gem_obj;
    int ret;

    if (!buf_size || !buf_paddr ||
        !PAGE_ALIGNED(buf_size) ||
        !PAGE_ALIGNED(buf_paddr)) {
        DRM_ERROR("dma_addr = %llu, size = %zu\n",
            (unsigned long long)buf_paddr, buf_size);
        return ERR_PTR(-EINVAL);
    }

    gem_obj = kzalloc(sizeof(struct drm_gem_cma_object), GFP_KERNEL);
    if (!gem_obj) {
        return ERR_PTR(-ENOMEM);
    }

    ret = drm_gem_object_init(drm, gem_obj, buf_size);
    if (ret) {
        goto error;
    }

    cma_obj = container_of(gem_obj, struct drm_gem_cma_object, base);
    cma_obj->paddr = buf_paddr;

    return &cma_obj->base;
error:
    kfree(cma_obj);
    return ERR_PTR(ret);
}

/* same as drm_gem_fb_alloc in drm_gem_framebuffer_helper.c */
static struct drm_framebuffer *
lombo_drm_gem_vbuf_fb_alloc(struct drm_device *dev,
    const struct drm_mode_fb_cmd2 *mode_cmd,
    struct drm_gem_object **obj, unsigned int num_planes,
    const struct drm_framebuffer_funcs *funcs)
{
    struct drm_framebuffer *fb;
    int ret, i;

    fb = kzalloc(sizeof(*fb), GFP_KERNEL);
    if (!fb) {
        return ERR_PTR(-ENOMEM);
    }

    drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);

    for (i = 0; i < num_planes; i++) {
        fb->obj[i] = obj[i];
    }

    ret = drm_framebuffer_init(dev, fb, funcs);
    if (ret) {
        DRM_DEV_ERROR(dev->dev, "Failed to init framebuffer: %d\n",
            ret);
        kfree(fb);
        return ERR_PTR(ret);
    }

    return fb;
}

static void lombo_drm_gem_vbuf_fb_destroy(struct drm_framebuffer *fb)
{
    int i;

    for (i = 0; i < EI_ARRAY_SIZE(fb->obj); i++) {
        if (fb->obj[i]) {
            drm_gem_object_put_unlocked(fb->obj[i]);
        }
    }

    DRM_DEBUG_KMS("fb_id=%d\n", fb->base.id);
    drm_framebuffer_cleanup(fb);
    kfree(fb);
}

static const struct drm_framebuffer_funcs lombo_gem_vbuf_fb_funcs = {
    .destroy = lombo_drm_gem_vbuf_fb_destroy,
};

static struct drm_framebuffer *lombo_drm_gem_vbuf_fb_create(
    struct drm_device *drm, struct drm_file *file_priv,
    const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object *obj)
{
    const struct drm_format_info *info;
    struct drm_framebuffer *fb;
    struct drm_gem_object *objs[4] = { NULL };
    int i;

    info = drm_format_info(mode_cmd->pixel_format);
    if (!info) {
        DRM_ERROR("%s:drm_format_info fail\n", __func__);
        return ERR_PTR(-EINVAL);
    }

    for (i = 0; i < info->num_planes; i++) {
        drm_gem_object_get(obj);
        objs[i] = obj;
    }

    fb = lombo_drm_gem_vbuf_fb_alloc(drm, mode_cmd, objs, i,
            &lombo_gem_vbuf_fb_funcs);
    if (IS_ERR(fb)) {
        for (i = 0; i < info->num_planes; i++) {
            drm_gem_object_put_unlocked(obj);
        }
    }

    return fb;
}

/* helper apis */
static struct drm_framebuffer *lombo_drm_helper_vbuf_fb_create(
    struct drm_device *drm, struct drm_file *file_priv,
    const struct drm_mode_fb_cmd2 *mode_cmd,
    size_t buf_size, dma_addr_t buf_paddr)
{
    struct drm_gem_object *obj;
    struct drm_framebuffer *fb;

    obj = lombo_drm_gem_import_vbuf(drm, buf_size, buf_paddr);
    if (IS_ERR(obj)) {
        DRM_ERROR("gem import vbuf fail\n");
        return ERR_CAST(obj);
    }

    fb = lombo_drm_gem_vbuf_fb_create(drm, file_priv, mode_cmd, obj);
    drm_gem_object_put_unlocked(obj);

    return fb;
}
/*--------------------lombo vbuf drm fb stop-----------------------*/

static struct drm_property *find_property(struct drm_mode_object *obj, const EI_CHAR *prop_name)
{
    struct drm_object_properties *obj_prop = obj->properties;
    EI_U32 i = 0;
    struct drm_property *prop = EI_NULL;

    for (; i < obj_prop->count; i++) {
        struct drm_property *tmp = obj_prop->properties[i];

        if (!os_strcmp(tmp->name, prop_name)) {
            prop = tmp;
            break;
        }
    }

    return prop;
}

#define MDP_DRM_MODE_OBJECT_MAX_NUM     64
#define MDP_DRM_PROP_MAX_NUM   (32 * MDP_DRM_MODE_OBJECT_MAX_NUM)

static struct drm_mode_atomic_k *drm_mode_atomic_init_k(EI_VOID)
{
    struct drm_mode_atomic_k *atomic = EI_NULL;
    struct drm_mode_object **obj = EI_NULL;
    struct drm_property **prop = EI_NULL;
    uint32_t *prop_cnt = EI_NULL;
    uint64_t *prop_val = EI_NULL;

    atomic = os_kzalloc(sizeof(*atomic), os_gfp_kernel);
    if (!atomic) {
        EI_TRACE_DOSS(EI_DBG_ERR, "%s %d\n", __func__, __LINE__);
        goto out;
    }

    obj = os_kzalloc(MDP_DRM_MODE_OBJECT_MAX_NUM * sizeof(obj[0]), os_gfp_kernel);
    if (!obj) {
        EI_TRACE_DOSS(EI_DBG_ERR, "%s %d\n", __func__, __LINE__);
        goto out;
    }

    prop_cnt = os_kzalloc(MDP_DRM_MODE_OBJECT_MAX_NUM * sizeof(prop_cnt[0]), os_gfp_kernel);
    if (!prop_cnt) {
        EI_TRACE_DOSS(EI_DBG_ERR, "%s %d\n", __func__, __LINE__);
        goto out;
    }

    prop = os_kzalloc(MDP_DRM_PROP_MAX_NUM * sizeof(prop[0]), os_gfp_kernel);
    if (!prop) {
        EI_TRACE_DOSS(EI_DBG_ERR, "%s %d\n", __func__, __LINE__);
        goto out;
    }

    prop_val = os_kzalloc(MDP_DRM_PROP_MAX_NUM * sizeof(prop_val[0]), os_gfp_kernel);
    if (!prop_val) {
        EI_TRACE_DOSS(EI_DBG_ERR, "%s %d\n", __func__, __LINE__);
        goto out;
    }

    atomic->objs_ptr = obj;
    atomic->count_props_ptr = prop_cnt;
    atomic->props_ptr = prop;
    atomic->prop_values_ptr = prop_val;
    return atomic;
out:
    if (prop_val)
        os_kfree(prop_val);
    if (prop)
        os_kfree(prop);
    if (prop_cnt)
        os_kfree(prop_cnt);
    if (obj)
        os_kfree(obj);
    if (atomic)
        os_kfree(atomic);
    return EI_NULL;
}

static EI_VOID drm_mode_atomic_deinit_k(struct drm_mode_atomic_k *atomic)
{
    if (!atomic)
        return;
    if (atomic->objs_ptr)
        os_kfree(atomic->objs_ptr);
    if (atomic->count_props_ptr)
        os_kfree(atomic->count_props_ptr);
    if (atomic->props_ptr)
        os_kfree(atomic->props_ptr);
    if (atomic->prop_values_ptr)
        os_kfree(atomic->prop_values_ptr);
    os_kfree(atomic);
}

static int katomic_req_to_katomic_arg(struct drm_mode_katomic_req *req, struct drm_mode_atomic_k *atomic)
{
    EI_U32 i = 0, last_obj = 0, obj_prop_cnt = 0, prop_cnt = 0;
    struct drm_mode_katomic_req_item *item = EI_NULL;
    struct drm_mode_object *obj = atomic->objs_ptr[0];

    atomic->count_objs = 0;
    for (; i < req->cursor; i++) {
        item = req->items + i;
        obj = atomic->objs_ptr[last_obj];

        if (item->obj == obj) {
            atomic->props_ptr[prop_cnt] = item->prop;
            atomic->prop_values_ptr[prop_cnt] = item->value;
            prop_cnt++;
            obj_prop_cnt++;
            atomic->count_props_ptr[last_obj] = obj_prop_cnt;
            continue;
        }
        obj_prop_cnt = 0;
        atomic->objs_ptr[last_obj] = item->obj;
        atomic->props_ptr[prop_cnt] = item->prop;
        atomic->prop_values_ptr[prop_cnt] = item->value;
        prop_cnt++;
        obj_prop_cnt++;
        atomic->count_props_ptr[last_obj] = obj_prop_cnt;
        last_obj++;
    }
    atomic->count_objs = last_obj;

    return EI_SUCCESS;
}

static EI_VOID katomic_arg_clear(struct drm_mode_atomic_k *atomic)
{
    EI_U32 i = 0, prop_cnt = 0;

    for (; i < atomic->count_objs; i++) {
        atomic->objs_ptr[i] = EI_NULL;
        prop_cnt += atomic->count_props_ptr[i];
        atomic->count_props_ptr[i] = 0;
    }
    os_memset((void *)(&atomic->props_ptr[0]), 0, prop_cnt * sizeof(atomic->props_ptr[0]));
    os_memset((void *)(&atomic->prop_values_ptr[0]), 0, prop_cnt * sizeof(atomic->prop_values_ptr[0]));
    atomic->flags = 0;
    atomic->count_objs = 0;
    atomic->reserved = 0;
    atomic->user_data = 0;
}

static struct drm_mode_katomic_req *drm_mode_katomic_alloc(EI_VOID)
{
    struct drm_mode_katomic_req *req;

    req = os_kzalloc(sizeof(*req), os_gfp_kernel);
    if (!req) {
        EI_TRACE_DOSS(EI_DBG_ERR, "%s %d\n", __func__, __LINE__);
        return EI_NULL;
    }

    req->cursor = 0;
    req->size_items = 0;
    req->items = EI_NULL;

    return req;
}

static EI_VOID drm_mode_katomic_free(struct drm_mode_katomic_req *req)
{
    if (!req)
        return;

    if (req->items)
        os_kfree(req->items);
    os_kfree(req);
}

static void drm_mode_katomic_set_cursor(struct drm_mode_katomic_req *req, int cursor)
{
    if (req)
        req->cursor = cursor;
}

static int drm_mode_katomic_add_property(struct drm_mode_katomic_req *req,
    struct drm_mode_object *obj, struct drm_property *prop, uint64_t value)
{
    if (!req || !obj || !prop)
        return -EINVAL;

    if (req->cursor >= req->size_items) {
        const uint32_t item_size_inc = 1024 / sizeof(*req->items);
        struct drm_mode_katomic_req_item *new, *old = req->items;

        req->size_items += item_size_inc;
        new = os_kzalloc(req->size_items * sizeof(*req->items), os_gfp_kernel);
        if (!new) {
            req->size_items -= item_size_inc;
            return -ENOMEM;
        }
        os_memmove((void *)new, old, req->cursor * sizeof(*req->items));
        req->items = new;
        if (old) {
            os_kfree(old);
        }
    }

    req->items[req->cursor].obj = obj;
    req->items[req->cursor].prop = prop;
    req->items[req->cursor].value = value;
    req->cursor++;

    return req->cursor;
}

static EI_S32 vo_set_property(struct drm_mode_katomic_req *req, struct drm_mode_object *obj,
        const EI_CHAR *prop_name, uint64_t value)
{
    struct drm_property *prop = find_property(obj, prop_name);

    if (prop) {
        drm_mode_katomic_add_property(req, obj, prop, value);
    } else {
        EI_TRACE_DOSS(EI_DBG_ERR, "find_property %s failed\n", prop_name);
        return EI_ERR_VO_ILLEGAL_PARAM;
    }

    return EI_SUCCESS;
}

int doss_vo_atomic_commit(EI_VOID)
{
    int ret, i = 0;
    struct ei_drm_dev *pstDrmDev = DOSS_DRV_GET_DRM_CTX();
    struct drm_mode_atomic_k *arg = pstDrmDev->katomic;

    arg->flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
    katomic_req_to_katomic_arg(pstDrmDev->req, arg);
    ret = drm_mode_katomic(pstDrmDev->drm_dev, arg, pstDrmDev->file_priv);
    katomic_arg_clear(arg);
    drm_mode_katomic_set_cursor(pstDrmDev->req, 0);
    for (; i < VO_MAX_LAYER_NUM; i++) {
        if (pstDrmDev->fbs[i]) {
            drm_framebuffer_put(pstDrmDev->fbs[i]);
            pstDrmDev->fbs[i] = EI_NULL;
        }
    }

    return ret;
}

static bool parse_doss_dc_drm_res(struct ei_drm_dev *drm)
{
    struct drm_plane *plane, **planes, **planes_bak;
    int i, j;
    int dc_cnt = 1; /* fixme for multi pipeline */
    EI_U32 possible_crtcs;

    /* we only need fetch non-DRM_PLANE_TYPE_CURSOR */
    planes = os_kzalloc(VO_MAX_LAYER_NUM * sizeof(struct drm_plane *), os_gfp_kernel);
    if (!planes) {
        EI_TRACE_DOSS(EI_DBG_ERR, "alloc for planes fail\n");
        return EI_FALSE;
    }

    planes_bak = os_kzalloc(VO_MAX_LAYER_NUM * sizeof(struct drm_plane *), os_gfp_kernel);
    if (!planes) {
        EI_TRACE_DOSS(EI_DBG_ERR, "alloc for planes fail\n");
        os_kfree(planes);
        return EI_FALSE;
    }

    i = 0;
    for (j = 0; j < dc_cnt; j++) {
        possible_crtcs = 1 << j;

        drm_for_each_plane(plane, drm->drm_dev) {
            if (plane->type == DRM_PLANE_TYPE_CURSOR) {
                continue;
            }
            if (plane->possible_crtcs == possible_crtcs) {
                planes[i] = plane;
                planes_bak[i] = plane;
                i++;
            }
        }
    }
    drm->planes = planes;
    drm->planes_bak = planes_bak;

    return EI_TRUE;
}

int doss_drm_res_init(void)
{
    struct ei_drm_dev *drm = g_pstDrmDev;
    struct lombo_drv *drv;
    int ret = EI_SUCCESS;

    if (drm->bInited) {
        return ret;
    }

    if (drm->drm_dev) {
        drv = (struct lombo_drv *)drm->drm_dev->dev_private;
        drm->wb = drv->wb;
        drm->wb_funcs = drv->wb_funcs;
    }

    drm->fbs = os_kzalloc(VO_MAX_LAYER_NUM * sizeof(struct drm_framebuffer *), os_gfp_kernel);
    if (!drm->fbs) {
        EI_TRACE_DOSS(EI_DBG_ERR, "alloc for atomic commit fbs fail\n");
        ret = EI_ERR_VO_NO_MEM;
        goto ERR1;
    }

    if (!parse_doss_dc_drm_res(drm)) {
        EI_TRACE_DOSS(EI_DBG_ERR, "parse_doss_dc_drm_res fail\n");
        ret = EI_ERR_VO_ILLEGAL_PARAM;
        goto ERR1;
    }

    drm->katomic = drm_mode_atomic_init_k();
    drm->req = drm_mode_katomic_alloc();
    drm->bInited = EI_TRUE;

    return ret;
ERR1:
    os_kfree(drm);
    return ret;
}

static int parse_doss_drm_res(void)
{
    struct ei_drm_dev *drm = EI_NULL;
    struct device_node *root = EI_NULL, *disp_np = EI_NULL, *np = dev->of_node;
    struct platform_device *disp_pdev = EI_NULL;
    int ret = EI_SUCCESS;

    drm = os_kzalloc(sizeof(*drm), os_gfp_kernel);
    if (!drm) {
        EI_TRACE_DOSS(EI_DBG_ERR, "alloc for ei_drm_dev fail\n");
        return -EI_ERR_VO_NO_MEM;
    }
    g_pstDrmDev = drm;

    root = of_get_parent(np);
    if (root) {
        disp_np = of_get_child_by_name(root, "disp");
        if (of_find_property(disp_np, "probe_panel", NULL)) {
            drm->bSkipBoot = EI_TRUE;
        } else {
            drm->bSkipBoot = EI_FALSE;
        }
    } else {
        os_printk("parse root node failed\n");
        ret = EI_ERR_VO_NULL_PTR;
        goto ERR1;
    }

    if (disp_np) {
        disp_pdev = of_find_device_by_node(disp_np);
        if (disp_pdev) {
            drm->drm_dev = (struct drm_device *)dev_get_drvdata(&disp_pdev->dev);
        } else {
            os_printk("find disp pdev failed\n");
            ret = EI_ERR_VO_NULL_PTR;
            goto ERR1;
        }
    } else {
        os_printk("parse disp node failed\n");
        ret = EI_ERR_VO_NULL_PTR;
        goto ERR1;
    }

    dev_set_drvdata(dev, drm);

    return EI_SUCCESS;

ERR1:
    os_kfree(drm);
    return ret;
}

static int release_doss_drm_res(void)
{
    struct ei_drm_dev *drm = dev_get_drvdata(dev);

    drm_mode_katomic_free(drm->req);
    drm_mode_atomic_deinit_k(drm->katomic);
    if (drm && drm->fbs) {
        os_kfree(drm->fbs);
    }
    if (drm && drm->planes) {
        os_kfree(drm->planes);
    }
    if (drm && drm->planes_bak) {
        os_kfree(drm->planes_bak);
    }
    os_kfree(drm);

    return EI_SUCCESS;
}

EI_S32 doss_vo_get_drm_client_file(EI_BOOL en)
{
    struct ei_drm_dev *pstDrmDev = DOSS_DRV_GET_DRM_CTX();
    struct drm_device *drm_dev = pstDrmDev->drm_dev;
    struct drm_file *drm_file = EI_NULL;

    if (!en) {
        pstDrmDev->file_priv = EI_NULL;
        return EI_SUCCESS;
    }

    drm_file = list_first_entry(&drm_dev->filelist, struct drm_file, lhead);
    pstDrmDev->file_priv = drm_file;
    drm_file->atomic = 1;
    drm_file->universal_planes = 1;

    return EI_SUCCESS;
}

#ifndef CONFIG_DRM_FBDEV_EMULATION
static void mode_from_sync_info(struct drm_display_mode *mode, VO_SYNC_INFO_S *pstSyncInfo)
{
    mode->clock = pstSyncInfo->u32Pclk; /* in kHz */
    mode->hdisplay = pstSyncInfo->u16Hact; /* HACT */
    /* hsync_start = HACT + HFP = HT - HBP - HSW */
    mode->hsync_start = pstSyncInfo->u16Hact + pstSyncInfo->u16Hfb;
    /* hsync_end = HACT + HFP + HSW = HT - HBP */
    mode->hsync_end = pstSyncInfo->u16Hact + pstSyncInfo->u16Hfb + pstSyncInfo->u16Hpw;
    mode->htotal = pstSyncInfo->u16Hact + pstSyncInfo->u16Hfb +
        pstSyncInfo->u16Hpw + pstSyncInfo->u16Hbb;
    mode->vdisplay = pstSyncInfo->u16Vact; /* VACT */
    /* vsync_start = VACT + VFP */
    mode->vsync_start = pstSyncInfo->u16Vact + pstSyncInfo->u16Vfb;
    /* vsync_end = VACT + VFP + VSW */
    mode->vsync_end = pstSyncInfo->u16Vact + pstSyncInfo->u16Vfb + pstSyncInfo->u16Vpw;
    mode->vtotal = pstSyncInfo->u16Vact + pstSyncInfo->u16Vfb +
        pstSyncInfo->u16Vpw + pstSyncInfo->u16Vbb;
}

static EI_U32 find_pipeline_connectors(VO_INTF_TYPE_E enIntfType,
        struct drm_device *dev, struct drm_connector **cons)
{
    EI_U32 ret = 0;
    int connector_type = DRM_MODE_CONNECTOR_DSI;
    struct drm_connector *connector;
    struct drm_connector_list_iter conn_iter;

    switch (enIntfType) {
    case VO_INTF_MIPI:
    case VO_INTF_MIPI_SLAVE:
        connector_type = DRM_MODE_CONNECTOR_DSI;
        break;
    case VO_INTF_LVDS_JEIDA_24BIT:
    case VO_INTF_LVDS_JEIDA_18BIT:
    case VO_INTF_LVDS_VESA_24BIT:
        connector_type = DRM_MODE_CONNECTOR_LVDS;
        break;
    case VO_INTF_LCD:
    case VO_INTF_LCD_6BIT:
    case VO_INTF_LCD_8BIT:
    case VO_INTF_LCD_16BIT:
    case VO_INTF_LCD_18BIT:
    case VO_INTF_LCD_24BIT:
        connector_type = DRM_MODE_CONNECTOR_RGB;
        break;
    case VO_INTF_I8080_18BIT_RGB666:
    case VO_INTF_I8080_16BIT_RGB888:
    case VO_INTF_I8080_16BIT_RGB666:
    case VO_INTF_I8080_16BIT_RGB565:
    case VO_INTF_I8080_9BIT_RGB666:
    case VO_INTF_I8080_8BIT_RGB666:
    case VO_INTF_I8080_8BIT_RGB565:
    case VO_INTF_I8080_8BIT_RGB888:
        connector_type = DRM_MODE_CONNECTOR_CPU;
        break;
    case VO_INTF_BT656:
    case VO_INTF_BT1120:
    case VO_INTF_BT656_H:
    case VO_INTF_BT656_L:
        connector_type = DRM_MODE_CONNECTOR_BT;
        break;
    default:
        EI_TRACE_DOSS(EI_DBG_ERR, "unknown connector type[%d]\n", enIntfType);
        break;
    }

    drm_connector_list_iter_begin(dev, &conn_iter);
    drm_for_each_connector_iter(connector, &conn_iter) {
        if (connector->connector_type == connector_type) {
            *cons = connector;
            cons++;
            ret++;
        }
    }
    drm_connector_list_iter_end(&conn_iter);

    return ret;
}

static struct drm_display_mode *find_pipeline_mode(struct drm_connector *connector,
        VO_SYNC_INFO_S *pstSyncInfo)
{
    struct drm_display_mode *mode = EI_NULL, vo_mode;

    mode_from_sync_info(&vo_mode, pstSyncInfo);
    drm_mode_debug_printmodeline(&vo_mode);

    #ifndef CONFIG_DRM_FBDEV_EMULATION
    connector->funcs->fill_modes(connector, connector->dev->mode_config.max_width,
                                    connector->dev->mode_config.max_height);
    #endif

    list_for_each_entry(mode, &connector->modes, head) {
        if (mode->hdisplay == vo_mode.hdisplay && mode->vdisplay == vo_mode.vdisplay &&
                mode->clock == vo_mode.clock) {
            drm_mode_debug_printmodeline(mode);
            break;
        }
    }

    return drm_mode_duplicate(connector->dev, mode);
}
#endif

static EI_S32 to_fourcc_format(PIXEL_FORMAT_E format, EI_U32 *fourcc)
{
    EI_S32 ret = EI_SUCCESS;

    switch (format) {
    case PIX_FMT_RGB_444:
        *fourcc = DRM_FORMAT_XRGB4444;
        break;
    case PIX_FMT_RGB_555:
        *fourcc = DRM_FORMAT_XRGB1555;
        break;
    case PIX_FMT_RGB_565:
        *fourcc = DRM_FORMAT_RGB565;
        break;
    case PIX_FMT_RGB_888:
        *fourcc = DRM_FORMAT_RGB888;
        break;
    case PIX_FMT_BGR_444:
        *fourcc = DRM_FORMAT_XBGR4444;
        break;
    case PIX_FMT_BGR_555:
        *fourcc = DRM_FORMAT_XBGR1555;
        break;
    case PIX_FMT_BGR_565:
        *fourcc = DRM_FORMAT_BGR565;
        break;
    case PIX_FMT_BGR_888:
        *fourcc = DRM_FORMAT_BGR888;
        break;
    case PIX_FMT_ARGB_1555:
        *fourcc = DRM_FORMAT_ARGB1555;
        break;
    case PIX_FMT_ARGB_4444:
        *fourcc = DRM_FORMAT_ARGB4444;
        break;
    case PIX_FMT_ARGB_8565:
        *fourcc = DRM_FORMAT_RGB565_A8;
        break;
    case PIX_FMT_ARGB_8888:
        *fourcc = DRM_FORMAT_ARGB8888;
        break;
    case PIX_FMT_ABGR_1555:
        *fourcc = DRM_FORMAT_ABGR1555;
        break;
    case PIX_FMT_ABGR_4444:
        *fourcc = DRM_FORMAT_ABGR4444;
        break;
    case PIX_FMT_ABGR_8565:
        *fourcc = DRM_FORMAT_BGR565_A8;
        break;
    case PIX_FMT_ABGR_8888:
        *fourcc = DRM_FORMAT_ABGR8888;
        break;
    case PIX_FMT_RGB_BAYER_8BITS:
    case PIX_FMT_RGB_BAYER_10BITS:
    case PIX_FMT_RGB_BAYER_12BITS:
    case PIX_FMT_RGB_BAYER_14BITS:
    case PIX_FMT_RGB_BAYER_16BITS:
    case PIX_FMT_RGB_BAYER_20BITS:
    case PIX_FMT_RGB_BAYER_10BITS_CMP:
    case PIX_FMT_RGB_BAYER_12BITS_CMP:
    case PIX_FMT_RGB_BAYER_14BITS_CMP:
        ret = EI_ERR_VO_NOT_SUPPORT;
        break;
    case PIX_FMT_YUV_SEMIPLANAR_422:
        *fourcc = DRM_FORMAT_NV16;
        break;
    case PIX_FMT_YUV_SEMIPLANAR_420:
        *fourcc = DRM_FORMAT_NV12;
        break;
    case PIX_FMT_YUV_SEMIPLANAR_444:
        ret = EI_ERR_VO_NOT_SUPPORT;
        break;
    case PIX_FMT_YVU_SEMIPLANAR_422:
        *fourcc = DRM_FORMAT_NV61;
        break;
    case PIX_FMT_YVU_SEMIPLANAR_420:
        *fourcc = DRM_FORMAT_NV21;
        break;
    case PIX_FMT_YVU_SEMIPLANAR_444:
        ret = EI_ERR_VO_NOT_SUPPORT;
        break;
    case PIX_FMT_YUV_PLANAR_422:
        *fourcc = DRM_FORMAT_YUV422;
        break;
    case PIX_FMT_YUV_PLANAR_420:
        *fourcc = DRM_FORMAT_YUV420;
        break;
    case PIX_FMT_YUV_PLANAR_444:
        *fourcc = DRM_FORMAT_YUV444;
        break;
    case PIX_FMT_YVU_PLANAR_422:
        *fourcc = DRM_FORMAT_YVU422;
        break;
    case PIX_FMT_YVU_PLANAR_420:
        *fourcc = DRM_FORMAT_YVU420;
        break;
    case PIX_FMT_YVU_PLANAR_444:
        *fourcc = DRM_FORMAT_YVU444;
        break;
    case PIX_FMT_YUYV_PACKAGE_422:
        *fourcc = DRM_FORMAT_YVYU;
        break;
    case PIX_FMT_YVYU_PACKAGE_422:
        *fourcc = DRM_FORMAT_YUYV;
        break;
    case PIX_FMT_UYVY_PACKAGE_422:
        *fourcc = DRM_FORMAT_UYVY;
        break;
    case PIX_FMT_VYUY_PACKAGE_422:
        *fourcc = DRM_FORMAT_VYUY;
        break;
    case PIX_FMT_YYUV_PACKAGE_422:
    case PIX_FMT_YYVU_PACKAGE_422:
    case PIX_FMT_UVYY_PACKAGE_422:
    case PIX_FMT_VUYY_PACKAGE_422:
    default:
        ret = EI_ERR_VO_NOT_SUPPORT;
        break;
    }

    return ret;
}

static struct drm_framebuffer *doss_vo_create_fb_from_frame_info(VIDEO_FRAME_INFO_S *pstFrmInfo)
{
    size_t buf_size;
    dma_addr_t buf_paddr;
    struct drm_mode_fb_cmd2 mode_cmd = { 0 };
    int i;
    ROTATION_E enRot = ROTATION_0;
    EI_U32 u32Width, u32Height, u32PlaneNum, au32Lstr[3] = { 0 }, au32Offset[3] = { 0 };

    if (!pstFrmInfo || !EI_VBUF_CHECK_BUFFER(pstFrmInfo->u32BufferId)) {
        return EI_NULL;
    }
    enRot = pstFrmInfo->stVFrame.enRotation;

    if (doss_vo_get_vbuf_wh(pstFrmInfo, &u32Width, &u32Height)) {
        EI_TRACE_DOSS(EI_DBG_ERR, "%s get frame buffer w/h failed\n", __func__);
        return EI_NULL;
    }

    buf_paddr = pstFrmInfo->stVFrame.u64PlanePhyAddr[0];
    buf_size = pstFrmInfo->stVFrame.u32TotalSize;
    to_fourcc_format(pstFrmInfo->stCommFrameInfo.enPixelFormat, &mode_cmd.pixel_format);
    u32PlaneNum = pstFrmInfo->stVFrame.u32PlaneNum;
    mode_cmd.width = u32Width;
    mode_cmd.height = u32Height;
    for (i = 0; i < u32PlaneNum; i++) {
        mode_cmd.offsets[i] = (!i) ? 0 : (pstFrmInfo->stVFrame.u64PlanePhyAddr[i] -
            pstFrmInfo->stVFrame.u64PlanePhyAddr[0]);
    }

    if (enRot == ROTATION_90 || enRot == ROTATION_270) {
        if (doss_vo_recaculate_rot_buffer_info(pstFrmInfo, u32Width, u32Height, au32Lstr, au32Offset)) {
            EI_TRACE_DOSS(EI_DBG_ERR, "recal rot buffer info err\n");
            return EI_NULL;
        } else {
            for (i = 0; i < u32PlaneNum; i++) {
                mode_cmd.pitches[i] = au32Lstr[i];
            }
        }
    } else {
        for (i = 0; i < u32PlaneNum; i++) {
            mode_cmd.pitches[i] = pstFrmInfo->stVFrame.u32PlaneStride[i];
        }
    }

    return lombo_drm_helper_vbuf_fb_create(DOSS_DRV_GET_DRM_CTX()->drm_dev,
            DOSS_DRV_GET_DRM_CTX()->file_priv, &mode_cmd, buf_size, buf_paddr);
}

static inline struct drm_plane *doss_vo_find_drm_plane(VO_LAYER VoLayer)
{
    return g_pstDrmDev ? g_pstDrmDev->planes[VoLayer] : EI_NULL;
}

EI_S32 lombo_fbdev_enable(VO_DEV VoDev, VO_INTF_TYPE_E enIntfType,
        VO_SYNC_INFO_S *pstSyncInfo, VIDEO_FRAME_INFO_S *pstFrmInfo)
{
    struct ei_drm_dev *pstDrmDev = DOSS_DRV_GET_DRM_CTX();
    struct drm_crtc *crtc = drm_crtc_from_index(pstDrmDev->drm_dev, VoDev);
    struct drm_framebuffer *fb = doss_vo_create_fb_from_frame_info(pstFrmInfo);
    struct drm_display_mode *mode = EI_NULL;
    EI_U32 i = 0;
    int ret = EI_SUCCESS;
    EI_BOOL bUserFB = EI_TRUE;
    struct drm_mode_modeinfo modeinfo = {0};
    struct drm_mode_object *obj = EI_NULL;
    struct drm_mode_katomic_req *req = pstDrmDev->req;

    #ifdef CONFIG_DRM_FBDEV_EMULATION
    struct lombo_drv *drv = (struct lombo_drv *)pstDrmDev->drm_dev->dev_private;
    struct drm_fb_helper *fbdev = drv->fbdev[VoDev];
    struct drm_mode_set *mode_set = &fbdev->crtc_info[0].mode_set;

    bUserFB = EI_FALSE;
    fb = mode_set->fb;
    crtc = mode_set->crtc;
    mode = mode_set->mode;
    pstDrmDev->num_connectors = mode_set->num_connectors;
    pstDrmDev->connectors = os_kzalloc(mode_set->num_connectors * sizeof(struct drm_connector *), os_gfp_kernel);
    if (!pstDrmDev->connectors) {
        EI_TRACE_DOSS(EI_DBG_ERR, "no mem for vo connectors\n");
        return EI_ERR_VO_NO_MEM;
    }
    for (i = 0; i < mode_set->num_connectors; i++) {
        pstDrmDev->connectors[i] = mode_set->connectors[i];
    }

    #else

    crtc = drm_crtc_from_index(pstDrmDev->drm_dev, VoDev);
    if (!crtc) {
        EI_TRACE_DOSS(EI_DBG_ERR, "no crtc\n");
        return EI_ERR_VO_NULL_PTR;
    }

    pstDrmDev->num_connectors = 2; /* at most 2 cons each pipeline for now */
    pstDrmDev->connectors = os_kzalloc(2 * sizeof(struct drm_connector *), os_gfp_kernel);
    if (!pstDrmDev->connectors) {
        EI_TRACE_DOSS(EI_DBG_ERR, "no mem for vo connectors\n");
        return EI_ERR_VO_NO_MEM;
    }

    pstDrmDev->num_connectors = find_pipeline_connectors(enIntfType, pstDrmDev->drm_dev, pstDrmDev->connectors);
    if (!pstDrmDev->num_connectors) {
        EI_TRACE_DOSS(EI_DBG_ERR, "no available connector\n");
        return EI_ERR_VO_ILLEGAL_PARAM;
    }

    mode = find_pipeline_mode(pstDrmDev->connectors[0], pstSyncInfo);
    if (!mode) {
        EI_TRACE_DOSS(EI_DBG_ERR, "no available mode\n");
        return EI_ERR_VO_NULL_PTR;
    }
    #endif

    drm_mode_convert_to_umode(&modeinfo, mode);
    pstDrmDev->blob = drm_property_create_blob(pstDrmDev->drm_dev, sizeof(modeinfo), &modeinfo);
    if (IS_ERR(pstDrmDev->blob)) {
        EI_TRACE_DOSS(EI_DBG_ERR, "create mode blob fail\n");
        return EI_ERR_VO_NULL_PTR;
    }

    if (pstDrmDev->bSkipBoot) {
        lombo_skip_boot_set(crtc, 1);
    }

    if (fb) {
        struct drm_plane *plane = EI_NULL;

        for (i = 0; i < VO_MAX_LAYER_NUM; i++) {
            plane = doss_vo_find_drm_plane(i);

            if (plane->type == DRM_PLANE_TYPE_PRIMARY)
                break;
        }
        obj = &plane->base;
        vo_set_property(req, obj, "CRTC_ID", crtc->base.id);
        vo_set_property(req, obj, "FB_ID", fb->base.id);
        vo_set_property(req, obj, "SRC_X", 0 << 16);
        vo_set_property(req, obj, "SRC_Y", 0 << 16);
        vo_set_property(req, obj, "SRC_W", mode->hdisplay << 16);
        vo_set_property(req, obj, "SRC_H", mode->vdisplay << 16);
        vo_set_property(req, obj, "CRTC_X", 0);
        vo_set_property(req, obj, "CRTC_Y", 0);
        vo_set_property(req, obj, "CRTC_W", mode->hdisplay);
        vo_set_property(req, obj, "CRTC_H", mode->vdisplay);
        EI_TRACE_DOSS(EI_DBG_INFO, "enable vodev by %s fb\n", bUserFB ? "user" : "fbdev");
    } else {
        EI_TRACE_DOSS(EI_DBG_INFO, "enable vodev by none fb\n");
    }

    obj = &crtc->base;
    vo_set_property(req, obj, "MODE_ID", pstDrmDev->blob->base.id);
    vo_set_property(req, obj, "ACTIVE", 1);
    for (i = 0; i < pstDrmDev->num_connectors; i++) {
        obj = &pstDrmDev->connectors[i]->base;
        vo_set_property(req, obj, "CRTC_ID", crtc->base.id);
    }
    ret = doss_vo_atomic_commit();

    if (bUserFB && fb) {
        drm_framebuffer_put(fb);
    }

    if (pstDrmDev->bSkipBoot) {
        pstDrmDev->bSkipBoot = EI_FALSE;
        lombo_skip_boot_set(crtc, 0);
    }

    return ret;
}

EI_S32 lombo_fbdev_disable(VO_DEV VoDev)
{
    struct ei_drm_dev *pstDrmDev = DOSS_DRV_GET_DRM_CTX();
    struct drm_crtc *crtc = drm_crtc_from_index(pstDrmDev->drm_dev, VoDev);
    struct drm_mode_object *obj = EI_NULL;
    struct drm_mode_katomic_req *req = pstDrmDev->req;
    EI_U32 i = 0;
    struct drm_plane *plane = EI_NULL;

    for (i = 0; i < VO_MAX_LAYER_NUM; i++) {
        plane = doss_vo_find_drm_plane(i);

        if (plane->type == DRM_PLANE_TYPE_PRIMARY)
            break;
    }
    obj = &plane->base;
    vo_set_property(req, obj, "CRTC_ID", 0);
    vo_set_property(req, obj, "FB_ID", 0);
    vo_set_property(req, obj, "SRC_X", 0 << 16);
    vo_set_property(req, obj, "SRC_Y", 0 << 16);
    vo_set_property(req, obj, "SRC_W", 0 << 16);
    vo_set_property(req, obj, "SRC_H", 0 << 16);
    vo_set_property(req, obj, "CRTC_X", 0);
    vo_set_property(req, obj, "CRTC_Y", 0);
    vo_set_property(req, obj, "CRTC_W", 0);
    vo_set_property(req, obj, "CRTC_H", 0);
    doss_vo_atomic_commit();

    if (!crtc) {
        EI_TRACE_DOSS(EI_DBG_ERR, "no crtc\n");
        return EI_ERR_VO_NULL_PTR;
    }

    obj = &crtc->base;
    vo_set_property(req, obj, "MODE_ID", 0);
    vo_set_property(req, obj, "ACTIVE", 0);
    for (; i < pstDrmDev->num_connectors; i++) {
        obj = &pstDrmDev->connectors[i]->base;
        vo_set_property(req, obj, "CRTC_ID", 0);
    }
    if (pstDrmDev->connectors) {
        os_kfree(pstDrmDev->connectors);
    }
    pstDrmDev->num_connectors = 0;

    return doss_vo_atomic_commit();
}

static inline struct drm_crtc *doss_vo_find_drm_crtc(VO_LAYER VoLayer)
{
    struct ei_drm_dev *pstDrmDev = DOSS_DRV_GET_DRM_CTX();

    return drm_crtc_from_index(pstDrmDev->drm_dev, VoLayer / VO_MAX_LAYER_IN_DEV);
}

EI_U32 doss_vo_reset_plane_priority(EI_VOID)
{
    struct ei_drm_dev *pstDrmDev = DOSS_DRV_GET_DRM_CTX();
    EI_U32 i;

    for (i = 0; i < VO_MAX_LAYER_NUM; i++) {
        pstDrmDev->planes[i] = pstDrmDev->planes_bak[i];
    }

    return EI_SUCCESS;
}

EI_U32 doss_vo_get_plane_priority(VO_LAYER VoLayer)
{
    struct drm_plane *plane;
    struct ei_drm_dev *pstDrmDev = DOSS_DRV_GET_DRM_CTX();
    EI_U32 i = 0;

    if (VoLayer >= VO_MAX_LAYER_NUM) {
        EI_TRACE_DOSS(EI_DBG_ERR, "layer invalid\n");
        return -EI_ERR_VO_INVALID_LAYERID;
    }
    plane = doss_vo_find_drm_plane(VoLayer);

    for (i = 0; i < VO_MAX_LAYER_NUM; i++) {
        if (plane == pstDrmDev->planes_bak[i]) {
            break;
        }
    }

    return i;
}

EI_U32 doss_vo_set_plane_priority(VO_LAYER VoLayer, EI_U32 priority)
{
    struct drm_plane *tmp, *plane;
    struct ei_drm_dev *pstDrmDev = DOSS_DRV_GET_DRM_CTX();

    if ((VoLayer >= VO_MAX_LAYER_NUM) || (priority >= VO_MAX_LAYER_NUM)) {
        EI_TRACE_DOSS(EI_DBG_ERR, "layer invalid\n");
        return -EI_ERR_VO_INVALID_LAYERID;
    }
    tmp = doss_vo_find_drm_plane(priority);
    plane = doss_vo_find_drm_plane(VoLayer);

    pstDrmDev->planes[VoLayer] = tmp;
    pstDrmDev->planes[priority] = plane;

    return EI_SUCCESS;
}

EI_S32 doss_set_csc(VO_LAYER VoLayer,       VO_CSC_S *pstCSC)
{
    struct drm_crtc *crtc = EI_NULL;
    struct drm_mode_object *obj = EI_NULL;
    struct ei_drm_dev *pstDrmDev = DOSS_DRV_GET_DRM_CTX();
    struct drm_mode_katomic_req *req = pstDrmDev->req;

    crtc = doss_vo_find_drm_crtc(VoLayer);
    if (!crtc) {
        return EI_FAILURE;
    }

    obj = &crtc->base;
    vo_set_property(req, obj, "color_enhance_enable", 1);
    vo_set_property(req, obj, "color_enhance_brightness", pstCSC->u32Luma);
    vo_set_property(req, obj, "color_enhance_saturation", pstCSC->u32Satuature);
    vo_set_property(req, obj, "color_enhance_contrast", pstCSC->u32Contrast);
    vo_set_property(req, obj, "cm_sat_red", pstCSC->u32SatuatureRed);
    vo_set_property(req, obj, "cm_sat_orange", pstCSC->u32SatuatureOrange);
    vo_set_property(req, obj, "cm_sat_yellow", pstCSC->u32SatuatureYellow);
    vo_set_property(req, obj, "cm_sat_green", pstCSC->u32SatuatureGreen);
    vo_set_property(req, obj, "cm_sat_cyan", pstCSC->u32SatuatureCyan);
    vo_set_property(req, obj, "cm_sat_blue", pstCSC->u32SatuatureBlue);
    vo_set_property(req, obj, "cm_sat_purple", pstCSC->u32SatuaturePurple);
    vo_set_property(req, obj, "cm_sat_magenta", pstCSC->u32SatuatureMagenta);

    return EI_SUCCESS;
}

static EI_U64 get_crtc_property_value(struct drm_crtc *crtc, const EI_CHAR *prop_name)
{
    EI_U64 val = 0;
    int ret = 0;
    struct drm_property *prop = EI_NULL;

    if (!crtc) {
        EI_TRACE_DOSS(EI_DBG_ERR, "null crtc\n");
        return 0;
    }

    prop = find_property(&crtc->base, prop_name);
    if (prop) {
        ret = crtc->funcs->atomic_get_property(crtc, EI_NULL, prop, &val);
        if (ret) {
            EI_TRACE_DOSS(EI_DBG_ERR, "get %s property value err[0x%x]", prop_name, ret);
            return 0;
        }
    }

    return val;
}

EI_S32 doss_get_csc(VO_LAYER VoLayer, VO_CSC_S *pstCSC)
{
    struct drm_crtc *crtc = doss_vo_find_drm_crtc(VoLayer);

    if (!crtc) {
        return EI_FAILURE;
    }
    pstCSC->u32Luma = get_crtc_property_value(crtc, "color_enhance_brightness");
    pstCSC->u32Satuature = get_crtc_property_value(crtc, "color_enhance_saturation");
    pstCSC->u32Contrast = get_crtc_property_value(crtc, "color_enhance_contrast");
    pstCSC->u32SatuatureRed = get_crtc_property_value(crtc, "cm_sat_red");
    pstCSC->u32SatuatureOrange = get_crtc_property_value(crtc, "cm_sat_orange");
    pstCSC->u32SatuatureYellow = get_crtc_property_value(crtc, "cm_sat_yellow");
    pstCSC->u32SatuatureGreen = get_crtc_property_value(crtc, "cm_sat_green");
    pstCSC->u32SatuatureCyan = get_crtc_property_value(crtc, "cm_sat_cyan");
    pstCSC->u32SatuatureBlue = get_crtc_property_value(crtc, "cm_sat_blue");
    pstCSC->u32SatuaturePurple = get_crtc_property_value(crtc, "cm_sat_purple");
    pstCSC->u32SatuatureMagenta = get_crtc_property_value(crtc, "cm_sat_magenta");

    return EI_SUCCESS;
}

EI_U32 doss_vo_recaculate_rot_buffer_info(VIDEO_FRAME_INFO_S *pstFrameInfo,
    EI_U32 u32RotWidth, EI_U32 u32RotHeight, EI_U32 *pu32Lstr, EI_U32 *pu32Offset)
{
    VIDEO_FRAME_FMT_S *pstVideoFmt = EI_NULL;
    COMMON_FRAME_INFO_S *pstCommFrameInfo = EI_NULL;
    EI_U32 i = 0, u32PlaneNum = 0, u32PlaneSizeValid = 0;

    pstCommFrameInfo = &pstFrameInfo->stCommFrameInfo;
    pstVideoFmt = doss_get_buf_fmt(pstCommFrameInfo->enPixelFormat);
    if (!pstVideoFmt) {
        EI_TRACE_DOSS(EI_DBG_ERR, "get picure format error pixfmt:%d!\n",
            pstCommFrameInfo->enPixelFormat);
        return EI_INVALID_VALUE;
    }
    if (!u32RotWidth || !u32RotHeight) {
        EI_TRACE_DOSS(EI_DBG_ERR, "error picture size W:%d H:%d!\n", u32RotWidth, u32RotHeight);
        return EI_INVALID_VALUE;
    }

    u32PlaneNum = pstVideoFmt->u8DataPlanes;

    for (i = 0; i < u32PlaneNum; ++i) {
        pu32Lstr[i] =
            EI_ALIGN_UP((u32RotWidth * pstVideoFmt->u8WidthDepth[i] + 7) >> 3, pstCommFrameInfo->u32Align);
        u32PlaneSizeValid = pu32Lstr[i] * u32RotHeight * pstVideoFmt->u8HeightDepth[i] / 8;
        pu32Offset[i] = EI_ALIGN_UP(u32PlaneSizeValid, PAGE_SIZE);
    }

    return EI_SUCCESS;
}

int doss_vo_setplane(VO_LAYER VoLayer, VIDEO_FRAME_INFO_S *pstFrmInfo,
    int32_t crtc_x, int32_t crtc_y, uint32_t crtc_w, uint32_t crtc_h,
    uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h)
{
    struct ei_drm_dev *pstDrmDev = DOSS_DRV_GET_DRM_CTX();
    struct drm_mode_katomic_req * req = pstDrmDev->req;
    struct drm_plane *plane = doss_vo_find_drm_plane(VoLayer);
    struct drm_mode_object *obj = &plane->base;
    struct drm_framebuffer *fb = doss_vo_create_fb_from_frame_info(pstFrmInfo);
    struct drm_crtc *crtc = fb ? doss_vo_find_drm_crtc(VoLayer) : EI_NULL;

    if (EI_VBUF_CHECK_BUFFER(pstFrmInfo->u32BufferId) && IS_ERR_OR_NULL(fb)) {
        EI_TRACE_DOSS(EI_DBG_ERR, "get fb failed\n");
        return EI_ERR_VO_ILLEGAL_PARAM;
    }

    if (!fb && plane->type == DRM_PLANE_TYPE_PRIMARY) {
        EI_TRACE_DOSS(EI_DBG_INFO, "skip disable primary plane\n");
        return EI_SUCCESS;
    }

    vo_set_property(req, obj, "CRTC_ID", crtc ? crtc->base.id : 0);
    vo_set_property(req, obj, "FB_ID", fb ? fb->base.id : 0);
    vo_set_property(req, obj, "SRC_X", src_x);
    vo_set_property(req, obj, "SRC_Y", src_y);
    vo_set_property(req, obj, "SRC_W", src_w);
    vo_set_property(req, obj, "SRC_H", src_h);
    vo_set_property(req, obj, "CRTC_X", crtc_x);
    vo_set_property(req, obj, "CRTC_Y", crtc_y);
    vo_set_property(req, obj, "CRTC_W", crtc_w);
    vo_set_property(req, obj, "CRTC_H", crtc_h);

    if (fb) {
        pstDrmDev->fbs[VoLayer] = fb;
    }

    return EI_SUCCESS;
}

static int wbc_queue_buf(struct lombo_wb *wb, struct lombo_wb_funcs *funcs,
    struct wbc_buf *buf)
{
    struct lombo_wb_que_buf que_buf = { 0 };

    que_buf.fb = buf->fb;
    que_buf.release_fence = -1;
    que_buf.user_priv = (uint64_t)(uintptr_t)buf;
    return funcs->queue_buf(wb, &que_buf);
}

static EI_S32 wbc_check_in_user_buf(WBC_CTX_S *wbc, struct wbc_buf *buf)
{
    struct wbc_user_buf *ubuf;
    EI_U32 depth = wbc->depth;
    EI_U32 num = 0;

    if (depth == 0) {
        return EI_SUCCESS;
    }

    ubuf = os_kzalloc(sizeof(*ubuf), os_gfp_kernel);
    if (!ubuf) {
        EI_TRACE_DOSS(EI_DBG_ERR, "kzalloc for ubuf fail\n");
        return EI_FAILURE;
    }

    memcpy(&ubuf->info, &buf->info, sizeof(ubuf->info));
    OSAL_INIT_LIST_HEAD(&ubuf->node);
    EI_TRACE_DOSS(EI_DBG_DEBUG, "ubuf=0x%lx, bufid=%u\n",
        (unsigned long)ubuf, ubuf->info.u32BufferId);

    os_spin_lock(&wbc->user_buf_lock);
    if (wbc->user_buf_num < depth) {
        wbc_ref_add(ubuf->info.u32BufferId, EI_ID_DOSS);
        os_list_add(&ubuf->node, &wbc->user_buf_head);
        wbc->user_buf_num++;
        num = wbc->user_buf_num;
        ubuf = NULL;
    }
    os_spin_unlock(&wbc->user_buf_lock);
    if (num == 1) {
        os_wakeup(&wbc->user_buf_wait);
    }

    if (ubuf) {
        EI_TRACE_DOSS(EI_DBG_INFO, "kfree ubuf\n");
        os_kfree(ubuf);
        return EI_FAILURE;
    }

    return EI_SUCCESS;
}

static EI_S32 wbc_buf_get_and_import(struct wbc_buf *buf, VBUF_POOL pool)
{
    struct ei_drm_dev *pstDrmDev = DOSS_DRV_GET_DRM_CTX();
    struct drm_device *drm = pstDrmDev->drm_dev;
    struct drm_file *file_priv = pstDrmDev->file_priv;
    struct drm_mode_fb_cmd2 mode_cmd = { 0 };
    struct drm_framebuffer *fb = EI_NULL;
    VIDEO_FRAME_INFO_S *info;
    EI_S32 ret;
    EI_U32 i;

    ret = wbc_get_buffer(pool, EI_ID_DOSS, 1000, &buf->buf);
    if (ret != EI_SUCCESS) {
        EI_TRACE_DOSS(EI_DBG_ERR, "get buf fail\n");
        return ret;
    }

    info = &buf->info;
    info->u32BufferId = buf->buf;
    ret = wbc_get_frame_info(info);
    if (ret != EI_SUCCESS) {
        EI_TRACE_DOSS(EI_DBG_ERR, "get buf frame info fail\n");
        return ret;
    }

    mode_cmd.width = info->stCommFrameInfo.u32Width;
    mode_cmd.height = info->stCommFrameInfo.u32Height;
    to_fourcc_format(info->stCommFrameInfo.enPixelFormat, &mode_cmd.pixel_format);
    for (i = 0; i < info->stVFrame.u32PlaneNum; i++) {
        mode_cmd.pitches[i] = info->stVFrame.u32PlaneStride[i];
        mode_cmd.offsets[i] = i ? info->stVFrame.u32PlaneSize[i - 1] : 0;
    }
    fb = lombo_drm_helper_vbuf_fb_create(drm, file_priv,
            &mode_cmd, info->stVFrame.u32TotalSize,
            info->stVFrame.u64PlanePhyAddr[0]);
    if (IS_ERR(fb)) {
        EI_TRACE_DOSS(EI_DBG_ERR, "create fb by vbuf fail\n");
        return PTR_ERR(fb);
    }
    buf->fb = fb;

    return EI_SUCCESS;
}

static void wbc_buf_release(struct wbc_buf *buf)
{
    if (!buf->fb) {
        return;
    }

    drm_framebuffer_unreference(buf->fb);
    buf->fb = NULL;

    wbc_put_buffer(buf->buf, EI_ID_DOSS);
    buf->buf = 0;
}

WBC_CTX_S g_stWbcCtx;

EI_VOID wbc_worker(struct os_work_struct *work)
{
    struct ei_drm_dev *pstDrmDev = DOSS_DRV_GET_DRM_CTX();
    struct lombo_wb *wb = pstDrmDev->wb;
    struct lombo_wb_funcs *funcs = pstDrmDev->wb_funcs;
    struct drm_lombo_wb_set_para para;
    struct wbc_buf *pbuf;
    EI_U32 index = 0;
    struct drm_lombo_wb_dequeue_buf deq_buf;
    struct drm_lombo_wb_start_arg arg = { .count = 2 };
    EI_S32 ret;

    WBC_CTX_S *wbc = container_of(work, WBC_CTX_S, work);

    if (!wb || !funcs) {
        EI_TRACE_DOSS(EI_DBG_ERR, "no wb funcs\n");
        return;
    }

    if (!wbc->enable) {
        EI_TRACE_DOSS(EI_DBG_INFO, "wbc worker return due to wbc disable\n");
        return;
    }

    EI_TRACE_DOSS(EI_DBG_DEBUG, "%s\n", __func__);

    memset(wbc->wq_buf, 0, sizeof(wbc->wq_buf));

    /* prepare */
    para.key = LOMBO_WB_PARA_KEY_CREATE_FB_QUEUE;
    para.value = 2;
    funcs->set_para(wb, &para);

    /* setup and queue 1st buf */
    index = 0;
    pbuf = &wbc->wq_buf[index];
    ret = wbc_buf_get_and_import(pbuf, wbc->pool);
    if (ret != EI_SUCCESS) {
        EI_TRACE_DOSS(EI_DBG_ERR, "get and import vbuf fail\n");
        return;
    }
    wbc_queue_buf(wb, funcs, pbuf);

    while (wbc->enable) {
        EI_U32 pre = index;

        index = (index + 1) & 1;
        pbuf = &wbc->wq_buf[index];
        ret = wbc_buf_get_and_import(pbuf, wbc->pool);
        if (ret != EI_SUCCESS) {
            EI_TRACE_DOSS(EI_DBG_ERR, "get and import vbuf failed\n");
            return;
        }
        wbc_queue_buf(wb, funcs, pbuf);

        funcs->start(wb, &arg);

        deq_buf.fb_state = LOMBO_WB_FB_STATE_WRITTEN;
        ret = funcs->dequeue_buf(wb, &deq_buf);
        if (ret != EI_SUCCESS) {
            EI_TRACE_DOSS(EI_DBG_ERR, "dequeue buf failed\n");
            return;
        }
        pbuf = &wbc->wq_buf[pre];
        if ((uint64_t)(uintptr_t)pbuf != deq_buf.user_priv) {
            EI_TRACE_DOSS(EI_DBG_ERR, "deq buf not match\n");
        }

        wbc_check_in_user_buf(wbc, pbuf);

        if (wbc->sink.enModId) {
            wbc_notify_sink(&wbc->sink, 0, (EI_VOID *)pbuf->buf);
        }

        wbc_buf_release(pbuf);
    }

    /* dequeue last fb */
    deq_buf.fb_state = LOMBO_WB_FB_STATE_IDLE;
    ret = funcs->dequeue_buf(wb, &deq_buf);
    if (ret == EI_SUCCESS) {
        wbc_buf_release((struct wbc_buf *)(uintptr_t)deq_buf.user_priv);
    } else {
        EI_TRACE_DOSS(EI_DBG_ERR, "can't dequeue last buf\n");
    }

    /* stop and quit */
    funcs->stop(wb);
    EI_TRACE_DOSS(EI_DBG_DEBUG, "wbc_worker quit\n");

    return;
}

EI_S32 wbc_dev_check(EI_VOID)
{
    struct ei_drm_dev *pstDrmDev = DOSS_DRV_GET_DRM_CTX();
    struct lombo_wb_funcs *funcs = pstDrmDev->wb_funcs;
    struct lombo_wb *wb = pstDrmDev->wb;

    if (!funcs || !wb) {
        EI_TRACE_DOSS(EI_DBG_ERR, "no wb\n");
        return EI_FAILURE;
    }

    return EI_SUCCESS;
}

EI_S32 wbc_set_param(EI_U32 key, EI_U64 value)
{
    struct drm_lombo_wb_set_para para;
    struct ei_drm_dev *pstDrmDev = DOSS_DRV_GET_DRM_CTX();
    struct lombo_wb_funcs *funcs = pstDrmDev->wb_funcs;
    struct lombo_wb *wb = pstDrmDev->wb;

    para.key = key;
    para.value = value;
    return funcs->set_para(wb, &para);
}

static int ei_doss_probe(struct platform_device *pdev)
{
    struct device *ei_dev = &pdev->dev;

    dev = ei_dev;
    if (parse_doss_drm_res()) {
        EI_PRINT("parse_doss_drm_res failed\n");
        return EI_FAILURE;
    }
    DOSS_ModInit();

    os_printk("%s\n", "doss init ok");
    return 0;
}

static int ei_doss_remove(struct platform_device *pdev)
{
    DOSS_ModExit();
    release_doss_drm_res();

    os_printk("%s\n", "doss exit ok");
    return 0;
}

static const struct of_device_id ei_doss_match[] = {
    { .compatible = "ei, ei-doss" },
    {},
};

MODULE_DEVICE_TABLE(of, ei_doss_match);

static struct platform_driver ei_doss_driver = {
    .probe          = ei_doss_probe,
    .remove         = ei_doss_remove,
    .driver         =
    {
        .name           = "ei_doss",
        .of_match_table = ei_doss_match,
    },
};

os_module_platform_driver(ei_doss_driver);

MODULE_LICENSE("GPL");
#endif
